package com.fanghuaiming.seata.account.tcc.impl;

import com.fanghuaiming.seata.account.dao.mapper.SeataTccAccountMapper;
import com.fanghuaiming.seata.account.dao.model.SeataTccAccount;
import com.fanghuaiming.seata.account.tcc.SeataPhaseTccAccount;
import com.fanghuaiming.seata.account.tcc.idempotent.SeataTccAccountResultHolder;
import io.seata.rm.tcc.api.BusinessActionContext;
import java.math.BigDecimal;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

/****
 * @description: 订单tcc接口实现类
 * @version:1.0.0
 * @author fanghuaiming
 * @data Created in 2020/11/30 下午4:59
 *
 */
@Component
@Slf4j
public class SeataPhaseTccAccountImpl implements SeataPhaseTccAccount {

    /**
     * mapper
     */
    @Autowired
    private SeataTccAccountMapper seataTccAccountMapper;

    @Autowired
    private SeataTccAccountResultHolder seataTccAccountResultHolder;

    /**
     * @Description: 第一阶段
     *
     * @param:
     * @return:
     * @auther: fanghuaiming
     * @date: 2020/11/30 下午5:02
     * @version:1.0.0
     */
    @SuppressWarnings("all")
    @Transactional
    @Override
    public boolean prepareDecreaseAccount(BusinessActionContext businessActionContext, Long userId, BigDecimal money) {
        //保存标识--这里应该优化成抛异常也要存入全局唯一Xid
        seataTccAccountResultHolder.setResult(getClass(), businessActionContext.getXid(), "p");
        log.info("==========减少账户金额，第一阶段锁定金额,userId={} money={} 事务组Xid:{}==========",userId,money,businessActionContext.getXid());
        SeataTccAccount seataTccAccount = seataTccAccountMapper.selectById(userId);
        if (seataTccAccount.getResidue().compareTo(money) < 0) {
            throw new RuntimeException("账户金额不足");
        }
        /*
        余额-money
        冻结+money
         */
        seataTccAccountMapper.updateFrozen(userId, seataTccAccount.getResidue().subtract(money), seataTccAccount.getFrozen().add(money));
        if (Math.random() < 0.999) {
            throw new RuntimeException("模拟异常");
        }
        return true;
    }

    /**
     * @Description: 第二阶段
     *
     * @param:
     * @return:
     * @auther: fanghuaiming
     * @date: 2020/11/30 下午5:03
     * @version:1.0.0
     */
    @SuppressWarnings("all")
    @Transactional
    @Override
    public boolean commit(BusinessActionContext businessActionContext) {
        long userId = Long.parseLong(businessActionContext.getActionContext("userId").toString());
        BigDecimal money =  new BigDecimal(businessActionContext.getActionContext("money").toString());
        log.info("========== 即将开始Confirm--> 减少账户金额,第二阶段 提交,userId={}, money:{},事务组Xid:{}==========",userId,money,businessActionContext.getXid());
        //防止重复提交
        if (seataTccAccountResultHolder.getResult(getClass(), businessActionContext.getXid()) == null) {
            log.info("账户Confirm已经确认过");
            return true;
        }
        seataTccAccountMapper.updateFrozenToUsed(userId, money);
        log.info("========== 已经完成Confirm--> 减少账户金额,第二阶段 提交,userId={}, money:{},事务组Xid:{}==========",userId,money,businessActionContext.getXid());
        //删除标识
        seataTccAccountResultHolder.removeResult(getClass(), businessActionContext.getXid());
        return true;
    }

    /**
     * @Description: 第二阶段
     *
     * @param:
     * @return:
     * @auther: fanghuaiming
     * @date: 2020/11/30 下午5:03
     * @version:1.0.0
     */
    @SuppressWarnings("all")
    @Transactional
    @Override
    public boolean rollback(BusinessActionContext businessActionContext) {
        long userId = Long.parseLong(businessActionContext.getActionContext("userId").toString());
        BigDecimal money =  new BigDecimal(businessActionContext.getActionContext("money").toString());
        log.info("========== 即将开始Cancle--> 减少账户金额,第二阶段 回滚,userId={}, money:{},事务组Xid:{}==========",userId,money,businessActionContext.getXid());
        //防止重复提交
        if (seataTccAccountResultHolder.getResult(getClass(), businessActionContext.getXid()) == null) {
            log.info("账户Cancle已经取消过");
            return true;
        }

        seataTccAccountMapper.updateFrozenToResidue(userId, money);

        log.info("========== 已经完成Cancle--> 减少账户金额，第二阶段，回滚，userId={}, money:{}==========",userId,money);

        //删除标识
        seataTccAccountResultHolder.removeResult(getClass(), businessActionContext.getXid());
        return true;
    }
}
